package core;

import static org.junit.Assert.*;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import core.exception.InvalidConversionException;

public class AmountTest {
        
        static Amount test1, test2, test3, test4, test5, test6, test7;

        @BeforeClass
        public static void setUpBeforeClass() throws Exception {
        }

        @AfterClass
        public static void tearDownAfterClass() throws Exception {
        }

        @Before
        public void setUp() throws Exception {
                test1 = new Amount((float)8.3, UnitOfMeasurement.pints);
                test2 = new Amount((float)4.23, UnitOfMeasurement.kilograms);
                test3 = new Amount((float)8.3, UnitOfMeasurement.pints);
                test4 = new Amount((float)9.1, UnitOfMeasurement.grams);
                test5 = new Amount((float)1.1, UnitOfMeasurement.gallons);
                test6 = new Amount((float)1.0, UnitOfMeasurement.count);
                test7 = new Amount((float)7.0, UnitOfMeasurement.pounds);
        }

        @After
        public void tearDown() throws Exception {
                test1 = null;
                test2 = null;
                test3 = null;
                test4 = null;
                test5 = null;
                test6 = null;
                test7 = null;
        }

        @Test
        public void testAmount() {
                Amount testAmount = new Amount((float)2.13, UnitOfMeasurement.liters);
                assertEquals(testAmount.getQuantity(), (float)2.13, (float).01);
                assertEquals(testAmount.getUnit(), UnitOfMeasurement.liters);
        }

        @Test
        public void testGetQuantity() {
                assertEquals(test1.getQuantity(), (float)8.3, (float).01);
        }

        @Test
        public void testSetQuantity() {
                test1.setQuantity((float)34.5);
                assertEquals(test1.getQuantity(), (float)34.5, (float).01);
        }

        @Test
        public void testCanSetQuantity() {
                assertFalse(test1.canSetQuantity((float)-1.32));
                assertTrue(test1.canSetQuantity((float)4.429));
        }

        @Test
        public void testGetUnit() {
                assertEquals(test1.getUnit(), UnitOfMeasurement.pints);
        }

        @Test
        public void testSetUnitOfMeasurement() {
                test1.setUnitOfMeasurement(UnitOfMeasurement.grams);
                assertEquals(test1.getUnit(), UnitOfMeasurement.grams);
        }
        
        @Test
        public void testEquals() {
                assertFalse(test1.equals(test2));
                assertFalse(test2.equals(test1));
                assertTrue(test1.equals(test3));
                assertTrue(test3.equals(test1));
                assertTrue(test1.equals(test1));
                assertTrue(test2.equals(test2));
                assertTrue(test3.equals(test3));
        }
        
        @Test
        public void testAdd() {
                try {
                        test1.add(test5);
                } catch (InvalidConversionException e) {
                        e.printStackTrace();
                        fail("InvalidConversionException");
                }
                
                assertEquals(test1.getUnit(), UnitOfMeasurement.pints);
                assertEquals(test1.getQuantity(), 17.1F, .01);
                
                try {
                        test4.add(test2);
                } catch (InvalidConversionException e) {
                        e.printStackTrace();
                        fail("InvalidConversionException");
                }
                
                assertEquals(test4.getUnit(), UnitOfMeasurement.grams);
                assertEquals(test4.getQuantity(), 4239.1F, .01);
        }
        
        @Test (expected = InvalidConversionException.class)
        public void testSubtractException() throws InvalidConversionException
        {
            test1.subtract(test2);
        }
        
        @Test
        public void testSubtract() throws InvalidConversionException
        {
        	float accuracy = (float) 0.001;

        	Amount test6 = new Amount((float) 5.5, UnitOfMeasurement.quarts);
        	test6.subtract(test1);
        	assertEquals((float)1.35, test6.getQuantity(), accuracy);
        	assertTrue(test6.getUnit() == UnitOfMeasurement.quarts);

        	test6 = new Amount((float) 5.5, UnitOfMeasurement.quarts);
        	test1.subtract(test6);
        	assertEquals(test1.getQuantity(), 0, accuracy);
        	assertTrue(test1.getUnit() == UnitOfMeasurement.pints); 

        	test2.subtract(test2);
        	assertEquals(test2.getQuantity(), 0, accuracy);
        	assertTrue(test2.getUnit() == UnitOfMeasurement.kilograms);     
        }
        
        @Test
        public void testMultiply()
        {
        	Amount testAmt = test1.multiply(0);
        	assertTrue(testAmt.getQuantity() == 0);
        	assertTrue(testAmt.getUnit() == UnitOfMeasurement.pints);
        	assertTrue(test1.getQuantity() == (float)8.3);

        	testAmt = test2.multiply(1);
        	assertTrue(testAmt.getQuantity() == (float)4.23);
        	assertTrue(testAmt.getUnit() == UnitOfMeasurement.kilograms);

        	testAmt = test2.multiply((float) 3.79);
        	assertTrue(testAmt.getQuantity() == (float)16.0317);
        	assertTrue(testAmt.getUnit() == UnitOfMeasurement.kilograms);
        }
        
        @Test
        public void testDivide()
        {
        	Amount testAmt = test1.divide(2);
        	assertTrue(testAmt.getQuantity() == (float)4.15);
        	assertTrue(testAmt.getUnit() == UnitOfMeasurement.pints);
        	assertTrue(test1.getQuantity() == (float)8.3);

        	testAmt = test1.divide(1);
        	assertTrue(testAmt.getQuantity() == (float)8.3);
        	assertTrue(testAmt.getUnit() == UnitOfMeasurement.pints);
        	assertTrue(test1.getQuantity() == (float)8.3);

        	testAmt = test1.divide((float) 0.5);
        	assertTrue(testAmt.getQuantity() == (float)16.6);
        	assertTrue(testAmt.getUnit() == UnitOfMeasurement.pints);
        	assertTrue(test1.getQuantity() == (float)8.3);
        }
        
    	@Test
    	public void testAmountStringConstructor(){
    		try {
    			Amount temp = new Amount("abc","count");
    			temp.getUnit();
    			fail("Conversion from \"abc\" to float should have failed.");
    		}
    		catch (IllegalArgumentException e) {}

    		try{
    			Amount temp2 = new Amount("3.0","www");
    			temp2.getUnit();
    			fail("Conversion from \"www\" to unit should have failed");
    		}
    		catch(IllegalArgumentException e) {}
    		
    		Amount count =new Amount("1", "count");
    		assertEquals(count.getUnit(),UnitOfMeasurement.count);
    		
    		Amount fluidOunces = new Amount("1", "fluid ounces");
    		assertEquals(fluidOunces.getUnit(), UnitOfMeasurement.fluid_ounces);
    		
    		Amount pound = new Amount("1", "pounds");
    		assertEquals(pound.getUnit(), UnitOfMeasurement.pounds);
    		
    		Amount ounce = new Amount("1", "ounces");
    		assertEquals(ounce.getUnit(), UnitOfMeasurement.ounces);
    		
    		Amount gram = new Amount("1", "grams");
    		assertEquals(gram.getUnit(), UnitOfMeasurement.grams);
    		
    		Amount kilogram = new Amount("1", "kilograms");
    		assertEquals(kilogram.getUnit(), UnitOfMeasurement.kilograms);	
    		
    		Amount gallon = new Amount("1", "gallons");
    		assertEquals(gallon.getUnit(), UnitOfMeasurement.gallons);	
    		
    		Amount quart = new Amount("1", "quarts");
    		assertEquals(quart.getUnit(), UnitOfMeasurement.quarts);	
    		
    		Amount pint = new Amount("1", "pints");
    		assertEquals(pint.getUnit(), UnitOfMeasurement.pints);	
    		
    		Amount liter = new Amount("1", "liters");
    		assertEquals(liter.getUnit(), UnitOfMeasurement.liters);		
    	}  
    	
    	@Test
    	public void testToString() {
    		assertEquals(test1.toString(), "8.3 pints");
    		assertEquals(test2.toString(), "4.23 kilograms");
			assertEquals(test3.toString(), "8.3 pints");
			assertEquals(test4.toString(), "9.1 grams");
			assertEquals(test5.toString(), "1.1 gallons");
			assertEquals(test6.toString(), "1 count");
			assertEquals(test7.toString(), "7 pounds");	
    	}

}